#include<linux/module.h>
#include<linux/init.h>
#include<linux/platform_device.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<dt-bindings/pinctrl/samsung.h>
#include<linux/pinctrl/consumer.h>
#include<linux/miscdevice.h>
#include<linux/fs.h>
#include<linux/delay.h>
#include<linux/uaccess.h>
#include<linux/slab.h>
//#include<plat/gpio-cfg.h>
//#include<mach/gpio-exynos4.h>
#include"lcd1602cmd.h"
#define DRIVER_NAME "lcd1602"
#define DEVICE_NAME "lcd1602_ctl"
//int gpios_rs,gpios_rw,gpios_e,gpios_d0,gpios_d1,gpios_d2,gpios_d3,gpios_d4,gpios_d5,gpios_d6,gpios_d7;
char gpios_name[][11] = {"gpios_rs","gpios_rw","gpios_e","gpios_d0","gpios_d1","gpios_d2","gpios_d3","gpios_d4","gpios_d5","gpios_d6","gpios_d7"};
int gpios[11]={0};
static inline int my_test_bit(int nr,unsigned int data){
	return (data >> nr) & (0x1);
}
static inline void set_lcd_rs(int data)
{
	gpio_direction_output(gpios[0],(data == 0)?0:1);
}
static inline void set_lcd_rw(int data)
{
	gpio_direction_output(gpios[1],(data == 0)?0:1);
}
static inline void set_lcd_e(int data)
{
	gpio_direction_output(gpios[2],(data == 0)?0:1);
}
static void set_lcd_data(unsigned int data){
	int i;
	for(i = 0;i < 8 ;i++)
	{
		gpio_direction_output(gpios[i+3],my_test_bit(i,data));
	}
	
}
static int get_lcd_data(void){
	int i;
	int ret,result =0;
	for(i = 0;i < 8 ;i++)
	{
		ret = gpio_direction_input(gpios[i+3]);
		result = result & (ret<< i);
	}
	return result;
}
static void lcd_cursor_reset(void)
{
	set_lcd_rs(0);
	set_lcd_rw(0);
	set_lcd_e(1);
	set_lcd_e(0);
	set_lcd_data(0x2);
}
static void lcd_cls(void)
{
	set_lcd_rs(0);
	set_lcd_rw(0);
	set_lcd_e(1);
	set_lcd_e(0);
	set_lcd_data(0x1);
}
static void lcd_setmode(unsigned int data)
{
	set_lcd_rs(0);
	set_lcd_rw(0);
	set_lcd_e(1);
	set_lcd_e(0);
	set_lcd_data( (1<<2) & data);
}
static void lcd_disp_ctl(unsigned int data){

	set_lcd_rs(0);
	set_lcd_rw(0);
	set_lcd_e(1);
	set_lcd_e(0);	
	set_lcd_data( (1<<4) & data);
}
static void lcd_screen_ctl(unsigned int data){

	set_lcd_rs(0);
	set_lcd_rw(0);	
	set_lcd_data( (1<<5) & data);
}
static void lcd_function_ctl(unsigned int data){

	set_lcd_rs(0);
	set_lcd_rw(0);	
	set_lcd_e(1);
	set_lcd_e(0);
	set_lcd_data( (1<<6) & data);
}
static void lcd_cgram_ctl(unsigned int data){

	set_lcd_rs(0);
	set_lcd_rw(0);
	set_lcd_e(1);
	set_lcd_e(0);	
	set_lcd_data( (1<<7) & data);
}
static void lcd_ddram_ctl(unsigned int data){

	set_lcd_rs(0);
	set_lcd_rw(0);
	set_lcd_e(1);
	set_lcd_e(0);	
	set_lcd_data( (1<<8) & data);
}
static long  lcd_bf_ac_addree(void)
{
	set_lcd_rs(0);
	set_lcd_rw(1);
	set_lcd_e(1);
	
	return get_lcd_data();
}
static void lcd_write_ddram(unsigned int data)
{
		set_lcd_rs(1);
		set_lcd_rw(0);
		set_lcd_e(1);
		set_lcd_e(0);
		set_lcd_data(data);
}
static int lcd_read_ddram(void)
{
		set_lcd_rs(1);
		set_lcd_rw(1);
		set_lcd_e(1);
		return get_lcd_data();
}
static int lcd1602_open (struct inode * node, struct file * flip){
	printk("Enter lcd1602_open\n");
	return nonseekable_open(node,flip);
}
static int lcd1602_release (struct inode * node, struct file * flip)
{
	printk("Enter lcd1602_release\n");
	return 0;
}

static void lcd_write_command(unsigned char data)
{
		set_lcd_rs(0);
		set_lcd_rw(0);
		set_lcd_e(1);
		set_lcd_e(0);
		set_lcd_data(data);
		udelay(40);
}
static void lcd_write_data( char data)
{
		set_lcd_rs(1);
		set_lcd_rw(0);
		set_lcd_e(1);
		set_lcd_e(0);
		set_lcd_data(data);
		udelay(40);
}
static void lcd_read_data(char *p)
{
	set_lcd_rs(1);
	set_lcd_rw(1);
	set_lcd_e(1);
	*p  = get_lcd_data();
	udelay(40);
}
int lastIndex = 0;
static ssize_t lcd1602_write (struct file * flip, const char __user * buf, size_t size, loff_t * off)
{
	char *p ;
	int i;
	unsigned char addr;
	
	p = kmalloc(size,GFP_KERNEL);
	copy_from_user(p,buf,size);
	//lcd_write_command(0);
	//lcd_write_data(p);
	
	for(i = 0 ; i < size ;i++)
	{
		if( i % 16 == 0)
		{
				addr = i;
		}else{
				addr = 0x40 + i;
		}
		lcd_write_command(addr);
		lcd_write_data(*p);
		p++;
	}
	kfree(p);
	return i;
}
static ssize_t lcd1602_read(struct file * flip, char __user * buf, size_t size, loff_t * off){
	char * p;
	int i;
	unsigned char addr;
	p = kmalloc(size,GFP_KERNEL);
	for(i = 0 ; i < size ;i++)
	{
		if( i % 16 == 0)
		{
				addr = i;
		}else{
				addr = 0x40 + i;
		}
		lcd_write_command(addr);
		lcd_read_data(p);
		p++;
	}
	copy_to_user(buf,p,size);
	kfree(p);
	return 0;
}
static long lcd1602_ioctl(struct file * flip, unsigned int cmd, unsigned long data){
		switch(cmd){
		case CLEAR:
			lcd_cls();
			break;
		case CURSOR_RESET:
			lcd_cursor_reset();
			break;
		case SET_MODE:
			lcd_setmode(data);
			break;
		case DISPLAY_CTL:
			lcd_disp_ctl(data);
			break;
		case SCREEN_MOVE_DIRECTION:
			lcd_screen_ctl(data);
			break;
		case FUNCTION_CTL:
			lcd_function_ctl(data);
			break;
		case CGRAM_CTL:
			lcd_cgram_ctl(data);
			break;
		case DDRAM_CTL:
			lcd_ddram_ctl(data);
			break;
		case BF_AC_ADDRESS:
			return lcd_bf_ac_addree();
			break;
		case WRITE_DDRAM:
			lcd_write_ddram(data);
			break;
		case READ_DDRAM:
			return lcd_read_ddram();
			break;
		default:
			printk("unkonow command\n");
		}
		return 0;
}
static struct file_operations lcd_fops ={
	.owner = THIS_MODULE,
	.open = lcd1602_open,
	.release = lcd1602_release,
	.write = lcd1602_write,
	.read = lcd1602_read,
	.unlocked_ioctl = lcd1602_ioctl,
};
static struct miscdevice lcd1602dev ={
	.minor = MISC_DYNAMIC_MINOR,
	.name = DRIVER_NAME,
	.fops = &lcd_fops,
};

static int lcd1602_probe(struct platform_device *pdev)
{
	
	struct device_node * node;
	int i,ret;
	printk("Enter lcd1602_probe\n");
	node = pdev->dev.of_node;
	for(i = 0; i < 11 ;i++)
	{
			gpios[i] = of_get_named_gpio(node,gpios_name[i],0);	
			ret =  gpio_request(gpios[i],gpios_name[i]);
			if(ret != 0)
			{
					printk("gpio_request :%s failed\n",gpios_name[i]);
					return 0;
			}
			//s3c_gpio_cfgpin(gpios[i],S3C_GPIO_OUTPUT);
	}
	
	misc_register(&lcd1602dev);
	printk("probe ok");
	return 0;
}
static int lcd1602_remove(struct platform_device *pdev)
{
	int i;
	printk("Enter lcd1602_remove\n");
	for(i =0;i < 11 ;i++)
	{
			gpio_free(gpios[i]);
	}
	
	misc_deregister(&lcd1602dev);
	printk("remove ok");
	return 0;
}
static const struct of_device_id of_lcd1602_dt_match[] ={
		{.compatible = DRIVER_NAME},{},
};
static struct platform_driver lcd1602_driver = {
	.probe = lcd1602_probe,
	.remove = lcd1602_remove,
	.driver  = {
			.name = DRIVER_NAME,
			.owner = THIS_MODULE,
			.of_match_table = of_lcd1602_dt_match,
		},
	
};
static int lcd1602_init(void)
{
	printk("Enter lcd1602_init\n");
	
	
	return platform_driver_register(&lcd1602_driver);
}
static void lcd1602_exit(void){
	printk("Enter lcd1602_exit\n");
	
	platform_driver_unregister(&lcd1602_driver);
}
module_init(lcd1602_init);
module_exit(lcd1602_exit);
MODULE_AUTHOR("jiangshuo");
MODULE_LICENSE("GPL");
